package dungeonPackage;

import static org.junit.Assert.*;

import java.math.BigDecimal;
import java.util.*;

import org.junit.*;

import squareBorderPackage.*;

import SquarePackage.*;
import TemperaturePackage.*;

/**
 * A class collecting tests for the class of dungeons.
 * 
 * @author Jessa Bekker & Esther Alberts
 * @version 1.0
 */
public class DungeonTest {

	/**
	 * Class variables referencing dungeons that do not change during the entire
	 * test case.
	 */
	private static Dungeon wetDungeon;

	/**
	 * Class variables referencing squares that do not change during the entire
	 * test case.
	 */
	private static Square wetSquare, square1, square2, square3, square4,
			square5;

	/**
	 * Class variables referencing orientations that do not change during the
	 * entire test case.
	 */
	private static Orientation max, sameOrientation, outside, orientation1,
			orientation2, orientation3, orientation4, orientation5,
			orientation6;

	/**
	 * Class variables referencing dungeons that may change during the test
	 * case.
	 */
	private Dungeon defaultDungeon;

	/**
	 * Class variables referencing squares that may change during the test case.
	 */
	private Square square6, square7, square8, square9, square10;

	/**
	 * set up an immutable test fixture.
	 * 
	 * @throws Exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		Orientation max = new Orientation(10, 10, 10);
		Orientation sameOrientation = new Orientation(1, 1, 1);
		Orientation outside = new Orientation(11, 12, 13);
		Orientation orientation1 = new Orientation(1, 2, 3);
		Orientation orientation2 = new Orientation(5, 3, 6);
		Orientation orientation3 = new Orientation(2, 5, 6);
		Orientation orientation4 = new Orientation(4, 5, 7);
		Orientation orientation5 = new Orientation(1, 1, 3);
		Orientation orientation6 = new Orientation(2, 1, 3);
		wetDungeon = new Dungeon(max);
		wetSquare = new Square(new Temperature(10, TemperatureUnit.CELSIUS),
				BigDecimal.TEN);
		square1 = new Square();
		square2 = new Square();
		square3 = new Square();
		square4 = new Square();
		square5 = new Square();
		wetSquare.setBorderAt(Direction.FLOOR, new Wall(true));
		square1.setBorderAt(Direction.FLOOR, new Wall(true));
		square2.setBorderAt(Direction.FLOOR, new Wall(true));
		square3.setBorderAt(Direction.FLOOR, new Wall(true));
		square4.setBorderAt(Direction.FLOOR, new Wall(true));
		wetDungeon.setSquareAt(orientation1, square1);
		wetDungeon.setSquareAt(orientation2, square2);
		wetDungeon.setSquareAt(orientation3, square3);
		wetDungeon.setSquareAt(orientation4, square4);
		wetDungeon.setSquareAt(orientation5, square5);
	}

	/**
	 * set up a mutable test fixture.
	 * 
	 * @throws Exception
	 */
	@Before
	public void setUp() throws Exception {
		defaultDungeon = new Dungeon(max);
		square6 = new Square();
		square7 = new Square();
		square8 = new Square();
		square9 = new Square();
		square10 = new Square();
		defaultDungeon.setSquareAt(orientation1, square6);
		defaultDungeon.setSquareAt(orientation2, square7);
		defaultDungeon.setSquareAt(orientation3, square8);
		defaultDungeon.setSquareAt(orientation4, square9);
		defaultDungeon.setSquareAt(orientation5, square10);
	}

	@Test
	public void initializeObject_LegalCase() {
		Dungeon dungeon = new Dungeon(new Orientation(10, 20, 30));
		assertTrue(dungeon.getMaxCoordinates().equals(
				new Orientation(10, 20, 30)));
	}

	@Test(expected = IllegalArgumentException.class)
	public void initializeObject_IllegalCase() throws Exception {
		Dungeon dungeon = new Dungeon(null);
	}

	@Test
	public void canHaveAsMaxCoordinates_TrueBigger() {
		assertTrue(defaultDungeon.canHaveAsMaxCoordinates(new Orientation(20,
				30, 40)));
	}

	@Test
	public void canHaveAsMaxCoordinates_TrueSame() {
		assertTrue(defaultDungeon.canHaveAsMaxCoordinates(new Orientation(10,
				10, 10)));
	}

	@Test
	public void canHaveAsMaxCoordinates_FalseNull() {
		assertFalse(defaultDungeon.canHaveAsMaxCoordinates(null));
	}

	@Test
	public void canHaveAsMaxCoordinates_FalseSmaller() {
		assertFalse(defaultDungeon.canHaveAsMaxCoordinates(new Orientation(9,
				10, 10)));
	}

	@Test
	public void setMaxCoordinates_LegalCase() {
		defaultDungeon.setMaxCoordinates(new Orientation(20, 30, 40));
		assertTrue(defaultDungeon.getMaxCoordinates().equals(
				new Orientation(20, 30, 40)));
	}

	@Test(expected = IllegalArgumentException.class)
	public void setMaxCoordinates_IllegalCase() throws Exception {
		defaultDungeon.setMaxCoordinates(new Orientation(5, 5, 5));
	}

	@Test(expected = IllegalArgumentException.class)
	public void getSquareAt_IllegalCase() throws Exception {
		defaultDungeon.getSquareAt(null);
	}

	@Test
	public void getSquareAt_orientationOutside() {
		assertTrue(defaultDungeon.getSquareAt(new Orientation(10, 11, 12)) == null);
	}

	@Test
	public void getSquareAt_noSquare() {
		assertTrue(defaultDungeon.getSquareAt(new Orientation(8, 8, 8)) == null);
	}

	@Test
	public void getSquareAt_aSquare() {
		assertTrue(defaultDungeon.getSquareAt(new Orientation(1, 2, 3)) == square6);
	}

	@Test
	public void getSquares_size() {
		assertTrue(defaultDungeon.getSquares().size() == 5);
	}

	@Test
	public void getSquares_contains() {
		Map<Orientation, Square> squares = defaultDungeon.getSquares();
		assertTrue(squares.containsKey(orientation1)
				&& squares.get(orientation1) == square6
				&& squares.containsKey(orientation2)
				&& squares.get(orientation1) == square7
				&& squares.containsKey(orientation3)
				&& squares.get(orientation1) == square8
				&& squares.containsKey(orientation4)
				&& squares.get(orientation1) == square9
				&& squares.containsKey(orientation5)
				&& squares.get(orientation1) == square10);
	}

	@Test
	public void isValidOrientation_null() {
		assertFalse(defaultDungeon.isValidOrientation(null));
	}

	@Test
	public void isValidOrientation_sameCoordinates() {
		assertFalse(defaultDungeon.isValidOrientation(sameOrientation));
	}

	@Test
	public void isValidOrientation_outside() {
		assertFalse(defaultDungeon.isValidOrientation(outside));
	}

	@Test
	public void isValidOrientation_True() {
		assertTrue(defaultDungeon.isValidOrientation(orientation3));
	}

	@Test
	public void isValidSquare_null() {
		assertFalse(defaultDungeon.isValidSquare(null));
	}

	@Test
	public void isValidSquare_invalidHumidity() {
		assertFalse(defaultDungeon.isValidSquare(new Square(new Temperature(50,
				TemperatureUnit.CELSIUS), null)));
	}

	@Test
	public void isValidSquare_True() {
		assertTrue(defaultDungeon.isValidSquare(square5));
	}

	@Test
	public void canHaveAsSquareAt_invalidSquare() {
		assertFalse(defaultDungeon.canHaveAsSquareAt(null, orientation3));
	}

	@Test
	public void canHaveAsSquareAt_invalidOrientation() {
		assertFalse(defaultDungeon.canHaveAsSquareAt(square3, null));
	}

	@Test
	public void canHaveAsSquareAt_repeatSquare() {
		assertFalse(defaultDungeon.canHaveAsSquareAt(square6, orientation3));
	}

	@Test
	public void canHaveAsSquareAt_repeatSquareSamePlace() {
		assertTrue(defaultDungeon.canHaveAsSquareAt(square6, orientation1));
	}

	@Test
	public void canHaveAsSquareAt_True() {
		assertTrue(defaultDungeon.canHaveAsSquareAt(square2, orientation3));
	}

	@Test(expected = IllegalArgumentException.class)
	public void hasSquareAt_IllegalCase() throws Exception {
		defaultDungeon.hasSquareAt(null);
	}

	@Test
	public void hasSquareAt_True() {
		assertTrue(defaultDungeon.hasSquareAt(orientation2));
	}

	@Test
	public void hasSquareAt_False() {
		assertFalse(defaultDungeon.hasSquareAt(sameOrientation));
	}

	@Test(expected = IllegalArgumentException.class)
	public void removeSquareAt_IllegalCase() throws Exception {
		defaultDungeon.removeSquareAt(null);
	}

	@Test
	public void removeSquareAt_LegalCase() {
		defaultDungeon.removeSquareAt(orientation1);
		assertFalse(defaultDungeon.hasSquareAt(orientation1));
	}

	@Test(expected = IllegalArgumentException.class)
	public void setSquareAt_IllegalCase() throws Exception {
		defaultDungeon.setSquareAt(orientation1, null);
	}

	@Test(expected = InvalidSquareAdditionException.class)
	public void setSquareAt_tooSlippery() throws Exception {
		wetDungeon.setSquareAt(orientation5, wetSquare);
	}

	@Test
	public void setSquareAt_Positioned() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(defaultDungeon.getSquareAt(orientation6) == wetSquare);
	}

	@Test
	public void setSquareAt_temperature() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(wetSquare.getTemperature().getNumeral() == 20);
	}

	@Test
	public void setSquareAt_humidity() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(wetSquare.getHumidity() == new BigDecimal(20));
	}

	@Test
	public void setSquareAt_sameSpace() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(wetSquare.getSpace().containsAll(square6.getSpace())
				&& square6.getSpace().containsAll(wetSquare.getSpace()));
	}

	@Test
	public void setSquareAt_spaceTemperature() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(wetSquare.getTemperature().equals(square6.getTemperature())
				&& square6.getTemperature().equals(wetSquare.getTemperature()));
	}

	@Test
	public void setSquareAt_spaceHumidity() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(wetSquare.getHumidity().equals(square6.getHumidity()));
	}

	@Test
	public void setSquareAt_sharedBorder() {
		defaultDungeon.setSquareAt(orientation6, wetSquare);
		assertTrue(wetSquare.getBorderAt(Direction.WEST) == square10
				.getBorderAt(Direction.EAST));
	}

	@Test
	public void getNbSlippery() {
		assertTrue(wetDungeon.getNumberSlippery() == 4);
	}

	@Test
	public void getSlipperyPercentage() {
		assertTrue(wetDungeon.getSlipperyPercentage() == 20);
	}

	@Test
	public void canAddSlipperyFlooring_True() {
		assertTrue(defaultDungeon.canAddSlipperyFlooring());
	}

	@Test
	public void canAddSlipperyFlooring_False() {
		assertTrue(wetDungeon.canAddSlipperyFlooring());
	}

	@Test
	public void getNeighbors() {
		Set<Orientation> neigbors = defaultDungeon.getNeighbors(orientation1);
		assertTrue(neigbors.size() == 1 && neigbors.contains(orientation5));
	}
}
